// 31 32-bit registers x1-x31, x0 hardwired to 0
// R-Type instructions
//   add, sub, and, or, slt
//   INSTR rd, rs1, rs2
//   Instr[31:25] = funct7 (funct7b5 & opb5 = 1 for sub, 0 for others)
//   Instr[24:20] = rs2
//   Instr[19:15] = rs1
//   Instr[14:12] = funct3
//   Instr[11:7]  = rd
//   Instr[6:0]   = opcode
// I-Type Instructions
//   lw, I-type ALU (addi, andi, ori, slti)
//   jalr, I-type ALU
//   lw:         INSTR rd, imm(rs1)
//   I-type ALU: INSTR rd, rs1, imm (12-bit signed)
//   Instr[31:20] = imm[11:0]
//   Instr[24:20] = rs2
//   Instr[19:15] = rs1
//   Instr[14:12] = funct3
//   Instr[11:7]  = rd
//   Instr[6:0]   = opcode
// S-Type Instruction
//   sw rs2, imm(rs1) (store rs2 into address specified by rs1 + immm)
//   Instr[31:25] = imm[11:5] (offset[11:5])
//   Instr[24:20] = rs2 (src)
//   Instr[19:15] = rs1 (base)
//   Instr[14:12] = funct3
//   Instr[11:7]  = imm[4:0]  (offset[4:0])
//   Instr[6:0]   = opcode
// B-Type Instruction
//   beq rs1, rs2, imm (PCTarget = PC + (signed imm x 2))
//   Instr[31:25] = imm[12], imm[10:5]
//   Instr[24:20] = rs2
//   Instr[19:15] = rs1
//   Instr[14:12] = funct3
//   Instr[11:7]  = imm[4:1], imm[11]
//   Instr[6:0]   = opcode
// J-Type Instruction
//   jal rd, imm  (signed imm is multiplied by 2 and added to PC, rd = PC+4)
//   Instr[31:12] = imm[20], imm[10:1], imm[11], imm[19:12]
//   Instr[11:7]  = rd
//   Instr[6:0]   = opcode
// U-Type Instruction
//   lui rd, imm  (signed imm is shifted by 12, rd = imm[31:12]<<12)
//   Instr[31:12] = imm[31:12]
//   Instr[11:7]  = rd
//   Instr[6:0]   = opcode

// Single-cycle implementation of RISC-V (RV32I)
// Implements a subset of the base integer instructions:
//    lw, sw
//    add, sub, and, or, slt, xor 
//    addi, andi, ori, slti
//    beq
//    jal
//    lui
// Exceptions, traps, and interrupts not implemented
// little-endian memory

//   Instruction  opcode    funct3    funct7
//   add          0110011   000       0000000
//   sub          0110011   000       0100000
//   and          0110011   111       0000000
//   or           0110011   110       0000000
//   slt          0110011   010       0000000
//   xor          0110011   100       0000000
//   addi         0010011   000       immediate
//   andi         0010011   111       immediate
//   ori          0010011   110       immediate
//   slti         0010011   010       immediate
//   beq          1100011   000       immediate
//   lw	          0000011   010       immediate
//   sw           0100011   010       immediate
//   jal          1101111   immediate immediate
//   lui          0110111   immediate immediate
`timescale 1ns/1ps
module top (
    input                   clk,
    //input       [31:0]      instr,
    input                   rst_n
    //output  reg [31:0]      pc
);

parameter WIDTH = 32;
parameter N =32;


///////////////PC///////////////

wire    [31:0] pc_next;
wire    [31:0] snpc;
wire    [31:0] dnpc;
///////////////inst_mem/////////////// 
wire    [31:0]  instr;
///////////////control_unit///////////////   
wire            zero;
wire            pcsrc;
wire            reg_write;
wire    [2:0]   imm_src;
wire    [1:0]   alu_srcA;
wire    [1:0]   alu_srcB;
wire            mem_write;
wire    [3:0]   mem_write_mask;
wire            mem_en;
wire    [1:0]   result_src;
wire    [2:0]   alu_control;
///////////////register_file/////////////// 
wire    [31:0]  RD1;
wire    [31:0]  RD2;
wire    [31:0]  WD3;
///////////////Extend///////////////
reg     [31:0]  imm_ext;
///////////////ALU///////////////
wire    [N-1:0] srcA;
wire    [N-1:0] srcB;
wire    [N-1:0] alu_result;
///////////////data_mem///////////////
wire    [31:0]  read_data;


reg     [31:0]  pc;
assign dnpc = alu_result;
assign snpc = pc + 4;
assign pc_next = pcsrc ? dnpc : snpc;
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        pc <= 32'h80000000;
    else
        pc <= pc_next;
end

always @(*) begin
    case(imm_src)
        3'b000: imm_ext = {{20{instr[31]}},instr[31:20]};                                //I-type 12bit signed immediate
        3'b001: imm_ext = {{20{instr[31]}},instr[31:25],instr[11:7]};                    //S-type 12bit signed immediate
        3'b010: imm_ext = {{20{instr[31]}},instr[7],instr[30:25],instr[11:8],1'b0};      //B-type 13bit signed immediate beq
        3'b011: imm_ext = {{12{instr[31]}},instr[19:12],instr[20],instr[30:21],1'b0};    //J-type 21bit signed immediate jal
        3'b100: imm_ext = {instr[31:12],12'b0};                                          //U-type 20bit signed immediate lui
        default: imm_ext = 0;
    endcase
end

// inst_mem #(WIDTH) u_inst_mem(
//     .A  (pc),
//     .RD (instr)
// );

control_unit u_control_unit(
    .zero           (zero           ),
    .op             (instr[6:0]     ),
    .funct3         (instr[14:12]   ),
    .funct7_5       (instr[30]      ),

    .pcsrc          (pcsrc          ),
    .reg_write      (reg_write      ),
    .imm_src        (imm_src        ),
    .alu_srcA       (alu_srcA       ),
    .alu_srcB       (alu_srcB       ),
    .mem_write      (mem_write      ),
    .mem_write_mask (mem_write_mask ),
    .mem_en         (mem_en         ),
    .result_src     (result_src     ),
    .alu_control    (alu_control    )
);

assign WD3 = result_src == 2'b00 ? alu_result :
             result_src == 2'b01 ? read_data :
             result_src == 2'b10 ? snpc :
             result_src == 2'b11 ? imm_ext :
             32'b0;
             
register_file u_register_file(
    .clk            (clk            ),
    .A1             (instr[19:15]   ),
    .A2             (instr[24:20]   ),
    .A3             (instr[11:7]    ),
    .WD3            (WD3            ),
    .WE3            (reg_write      ),
    .RD1            (RD1            ),
    .RD2            (RD2            )
);

assign srcB = alu_srcB == 2'b00 ? RD2 : 
              alu_srcB == 2'b01 ? imm_ext : 
              32'b0;
assign srcA = alu_srcA == 2'b00 ? pc : 
              alu_srcA == 2'b10 ? RD1 :
              32'b0;
alu  #(N) u_alu(
    .alu_control    (alu_control    ),    //000:ADD 001:Subtract 101:SLT 011:OR 010:AND
    .srcA           (srcA           ),
    .srcB           (srcB           ),
    .alu_result     (alu_result     ),
    .zero           (zero           )
);

data_mem #(WIDTH) u_data_mem(
    //.clk            (clk                    ),
    .A              (alu_result             ),
    .EN             (mem_en                 ),
    .WM             (mem_write_mask         ),
    .WE             (mem_write              ),
    .WD             (RD2                    ),
    .RD             (read_data              ),
    .pc             (pc                     ),
    .instr          (instr                  )
);

import "DPI-C" function void ebreak ();
always @(*) begin
    if(instr == 32'b0000000_00001_00000_000_00000_11100_11) 
        ebreak();       
end

endmodule
